/*
 *	Copyright (C) 2008 CRIMERE
 *	Copyright (C) 2008 Jean-Marc Mercier
 *	
 *	This file is part of OTS (optimally transported schemes), an open-source library
 *	dedicated to scientific computing. http://code.google.com/p/optimally-transported-schemes/
 *
 *	CRIMERE makes no representations about the suitability of this
 *	software for any purpose. It is provided "as is" without express or
 *	implied warranty.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#if !defined(_GenericAlgebra_)
#define _GenericAlgebra_

#include <src/miscellaneous/OS_basic.h>
#include <boost/operators.hpp>
#include <src/data/Operators.h>


template<class A, class R>
struct OS_negate;
template<class A, class R>
struct OS_plus;
template<class A, class R>
struct OS_minus;
template<class A, class R>
struct OS_multiply;
template<class A, class R>
struct OS_multiply2;
template<class A, class R>
struct OS_divide2;
template<class A, class R>
struct OS_divide;

/*! \brief Interface class of OS_Generic_Algebra

	We split apart data and virtual function from the main algorithmic part.
*/
template <class Algebra>
struct OS_Generic_Algebra_interface
{
		typedef typename OS_Generic_Algebra_interface interface_type;
		typedef typename Algebra interfaced_type;
		virtual ~OS_Generic_Algebra_interface(){};
		typedef smart_ptr<Algebra>						smart_ptr_type;

		virtual smart_ptr_type Create_Instance() const
		{
			if (first_)
				return first_->Create_Instance();
			return smart_ptr_type();
		};
		virtual bool equals (const Algebra & from) const {
			bool result = !( (first_ == 0) ^ (from.first_ == 0) );
			if (result == false) {
				return false;
			};
			if (first_ ) // thus (from.first_ also)
				return ( first_->equals(*from.first_ ) );
			return result;
		};
		inline virtual void output (std::ostream& o) const {
			if (first_)
				first_->output(o);
			
		};
		smart_ptr_type first_;
};
/*! Generic algebra for function operating on algebraic data set.
	
	
	Let us suppose that the element set \f$\{x \in \Omega_x\}\f$ 
	implement a basic set of operations (we considered +,-,*,/ inner operations and outer *,/, but it can be easily extensible to others operations).

	Let us define now \f$Op(\Omega_x,\Omega_y)\f$ the set of all function \f$f(x)=y\in \Omega_y\f$.
	Then this class implements an over \f$Op(\Omega_x,\Omega_y)\f$ :
	
	More precisely, let \f$f,g \in Op(\Omega_x,\Omega_y)\f$, this class implements the following operations : 
	-
	-f+g : x\rightarrow f(x)+g(x)	(inner addition)
	-f-g : x\rightarrow f(x)-g(x)	(inner substraction)
	-f*g : x\rightarrow f(x)*g(x)	(inner multiplication)			
	-f/g : x\rightarrow f(x)/g(x)   (inner division)
	-f | g : x\rightarrow f(g(z))	(composition with a function \f$g \in Op(\Omega_z,\Omega_x)\f$)
	- -f : x\rightarrow -f(x)		(negation)
	-f * \alpha		: x\rightarrow \f$f(x)*\alpha\f$	(outer multiplication)
	- f / \alpha	: x\rightarrow \f$f(x)/ \alpha\f$	(outer division)
	-f ^ g : x\rightarrow f(x)^g(x)	(xorable. Used to compute \f$f^g(\{S_i\}_i) = (\{f(S_i)*g(S_i)\}_i)\f$ )

*/
template<class A, class R, class V = smart_ptr<A> >
struct OS_Generic_Algebra :
	public outer_structure<A,R>
	, public operator_base<A,R>
	, public boost::arithmetic< OS_Generic_Algebra<A,R,V>
	,	boost::multiplicative< OS_Generic_Algebra<A,R,V>, typename outer_structure<A,R>::base_value_type
	> >
	,public boost::bitwise<OS_Generic_Algebra<A,R,V>>
	, public OS_Generic_Algebra_interface< OS_Generic_Algebra<A,R,V> >
	, public state_variable_traits<V >
	{
	public:
		typedef operator_base<A,R>								Base;
		typedef OS_Generic_Algebra								type;
		typedef OS_Generic_Algebra								Base_Cast;
		typedef OS_Generic_Algebra_interface					interface_type;
		virtual std::string get_name() const {return "OS_Generic_Algebra";};
		OS_Generic_Algebra(){};
		OS_Generic_Algebra(const OS_Generic_Algebra &from) {
				first_ = from.Create_Instance();
		};
		OS_Generic_Algebra& operator = (const OS_Generic_Algebra &from) {
			if (from.first_)
					first_ = from.first_->Create_Instance();
			return *this;
		};

		template <class T, class Data>
		smart_ptr_type Create_Instance_helper_arg(const Data * data) const
		{
			smart_ptr_type result(new T(*data));
			if (first_)
				result->first_ = first_->Create_Instance();
			return result;
		};
		template <class T>
		smart_ptr_type Create_Instance_helper() const
		{
			T* result = new T;
			if (first_)
				result->first_ = first_->Create_Instance();
			return smart_ptr_type(result);
		};
		OS_Generic_Algebra & operator-() {
			first_ = smart_ptr_type(new OS_negate<A,R>(first_));
			return *this;
		};
		OS_Generic_Algebra & operator +=(const OS_Generic_Algebra & from) {
			if (first_)
				first_ = smart_ptr_type(new OS_plus<A, R>(*first_,from));
			else first_ = from.Create_Instance();
			return *this;
		};
		OS_Generic_Algebra & operator ^=(const OS_Generic_Algebra & from) {
			if (first_)
				first_ = smart_ptr_type(new OS_xor<A, R>(*first_,from));
			else first_ = from.Create_Instance();
			return *this;
		};
		OS_Generic_Algebra & operator -=(const OS_Generic_Algebra & from) {
			if (first_)
				first_ = smart_ptr_type(new OS_minus<A, R>(*first_,from));
			else 
				first_ = smart_ptr_type(new OS_negate<A, R>(from));
			return *this;
		};
		OS_Generic_Algebra & operator *=(const OS_Generic_Algebra & from) {
			if (first_)
				first_ = smart_ptr_type(new OS_multiply<A, R>(*first_,from));
			return *this;
		};
		OS_Generic_Algebra & operator /=(const OS_Generic_Algebra & from) {
			if (first_)
				first_ = smart_ptr_type(new OS_divide<A, R>(*first_,from));
			return *this;
		};
		OS_Generic_Algebra & operator *=(const base_value_type & from) {
			if (first_)
				first_ = smart_ptr_type(new OS_multiply2<A, R>(*first_,from));
			return *this;
		};
		OS_Generic_Algebra & operator /=(const base_value_type & from) {
			if (first_)
				first_ = smart_ptr_type(new OS_divide2<A, R>(*first_,from));
			return *this;
		};
		inline bool operator == (const OS_Generic_Algebra & from) const {
			return OS_Generic_Algebra::equals(from);
		};
		inline bool operator != (const OS_Generic_Algebra & from) const {
			return !OS_Generic_Algebra::equals(from);
		};
		virtual result_type operator()(argument_type data) const {
			return (*first_)(data);
		};
		inline virtual void update () {
			if (first_) {
				first_->set_state_variable(get_state_variable());
			};
		};
		friend inline std::ostream& operator << (std::ostream& out, const OS_Generic_Algebra &from)
		{
					from.output(out);
					return out;
		};
	};

template<class A, class R>
struct OS_negate : public OS_Generic_Algebra<A,R>
	{
		typedef OS_Generic_Algebra<A,R>	Base;
		OS_negate(){};
		OS_negate(const Base &first) : Base(first)
		{};

		virtual std::string get_name() const {return "-";};
		virtual result_type operator()(argument_type data) const {
			return - (*first_)(data);
		};
		virtual smart_ptr_type Create_Instance() const {
			return Create_Instance_helper<OS_negate>();
		};
		virtual void output (std::ostream& o) const { 
			o<<"-";
			if (first_)
				first_->output(o);
	
		};
	};

template <class T>
struct OS_multiply2_interface 
{
	OS_multiply2_interface() : data_(0) {};
		virtual ~OS_multiply2_interface(){};
		typedef typename T::base_value_type OS_multiply2_data_type;
		OS_multiply2_interface(const typename T::base_value_type & data) : data_(data){};
		OS_multiply2_data_type data_;
};

template<class A, class R>
struct OS_multiply2 : public OS_Generic_Algebra<A,R>
	, public virtual OS_multiply2_interface<OS_Generic_Algebra<A,R>>
	{
		typedef OS_Generic_Algebra<A,R>			Base;
		typedef typename OS_multiply2_interface interface_type;
		OS_multiply2() {};
		OS_multiply2(const base_value_type & data) : OS_multiply2::interface_type(data){};
		OS_multiply2(const Base &first, const base_value_type& data ) : OS_multiply2::interface_type(data), Base(first)
		{};
		OS_multiply2(const OS_multiply2 & from) : data_(from.data_){};
		virtual std::string get_name() const {return "*";};
		virtual result_type operator()(argument_type data) const {
			return data_* (*first_)(data);
		};
		virtual smart_ptr_type Create_Instance() const {
			return Create_Instance_helper_arg<OS_multiply2,base_value_type>(&data_);
		};
		virtual bool equals (const Base & from) const {
			if (data_ != static_cast<const OS_multiply2&> (from).data_)
				return false;
			return Base::equals(from);
		};
		virtual void output (std::ostream& o) const { 
			if (first_)
				o << "("  ; first_->output(o);  o<< ")*" << data_;
			
		};
	};

template <class T>
struct OS_divide2_interface 
{
	OS_divide2_interface(): data_(0){};
		virtual ~OS_divide2_interface(){};
		typedef typename T::base_value_type OS_divide2_data_type;
		OS_divide2_interface(const typename T::base_value_type & data) : data_(data){};
		OS_divide2_data_type data_;
};
template<class A, class R>
struct OS_divide2 : public OS_Generic_Algebra<A,R>
	, public virtual OS_divide2_interface<OS_Generic_Algebra<A,R>>
	{
		typedef OS_Generic_Algebra<A,R>	Base;
		typedef OS_divide2_interface interface_type;
		OS_divide2(){};
		OS_divide2(const Base &first, const base_value_type& data ) : OS_divide2::interface_type(data), Base(first)
		{};
		OS_divide2(const base_value_type & data) : OS_divide2::interface_type(data){};
		OS_divide2(const OS_divide2 & from) : data_(from.data_){};
		virtual std::string get_name() const {return "/";};

		virtual result_type operator()(argument_type data) const {
			return (*first_)(data) / data_;
		};
		virtual smart_ptr_type Create_Instance() const {
			return Create_Instance_helper_arg<OS_divide2,base_value_type>(&data_);
		};
		virtual bool equals (const Base & from) const {
			if (data_ != static_cast<const OS_divide2&> (from).data_)
				return false;
			return Base::equals(from);
		};
		virtual void output (std::ostream& o) const { 
			if (first_)
				o << "("  ; first_->output(o);  o<< ")/" << data_;
			
		};
	};
template <class T>
struct OS_binary_function_interface
{
	OS_binary_function_interface(){};
	virtual ~OS_binary_function_interface(){};
	typedef typename T::smart_ptr_type OS_binary_function_interface_smart_ptr_type;
	OS_binary_function_interface_smart_ptr_type second_;
};

template<class A, class R>
struct OS_binary_function : public OS_Generic_Algebra<A,R>
	, public virtual OS_binary_function_interface<OS_Generic_Algebra<A,R>>
	{
		typedef OS_Generic_Algebra<A,R>	Base;
		typedef OS_binary_function_interface interface_type;
		typedef OS_Generic_Algebra<A,R>	Base_Cast;
		OS_binary_function(){};
		OS_binary_function(const Base &first) : Base(first)
		{};
		OS_binary_function(const Base &first, const Base &second) {
			first_ = first.Create_Instance();
			second_ = second.Create_Instance();
		};
		virtual std::string get_name() const {return "OS_binary_function";};
		virtual bool check_equal(const Base & from) const {
			const OS_binary_function & from_ = static_cast<const OS_binary_function&> (from);
			bool result = !( (first_ == 0) ^ (from_.first_ == 0) );
			if (result == false) 
				return false;

			if (first_ ) // thus (from.first_ also)
				result = first_->Base_Cast::equals(*from_.first_ );
			if (result == false) 
				return false;
			result = !( (second_ == 0) ^ (from_.second_ == 0) );
			if (result == false) 
				return false;
			if (second_ ) // thus (from.first_ also)
				return second_->Base_Cast::equals(*from_.second_ );
			else
				return true;

		};
		inline virtual void update () {
			if (first_) {
				first_->set_state_variable(get_state_variable());
			};
			if (second_) {
				second_->set_state_variable(get_state_variable());
			};
		};
		virtual bool equals (const Base & from) const {
			OS_DYNAMIC_CHECK(false, "to implement");
			return false;
		};

		virtual void output (std::ostream& o) const { 
			if (first_) {
				o << "(" ; first_->output(o);  o<< ")";
			};
			o << "--no operation--";
			if (second_) {
				o << "(" ; second_->output(o);  o<< ")";
			};
			

		};

		virtual result_type operator()(argument_type data) const {
			OS_DYNAMIC_CHECK(false,"OS_binary_function should not be intanciated");
			return result_type();
		};
		template <class T>
		smart_ptr_type Create_Instance_helper() const
		{
			T* result = new T;
			if (second_)
				result->second_ = second_->Create_Instance();
			if (first_)
				result->first_ = first_->Create_Instance();
			return smart_ptr_type(result);
		};
	};


template<class A, class R = A>
	struct OS_plus
: public OS_binary_function<A,R>
	{
		typedef OS_binary_function<A,R>	Base;
		typedef typename Base::Base_Cast	Base_Cast;
		OS_plus(){};
		OS_plus(const Base_Cast &first, const Base_Cast &second) 
			: Base(first,second)
		{};
		virtual bool equals (const Base_Cast & from) const {
			return check_equal(from);
		};
		virtual std::string get_name() const {return "+";};
		virtual void output (std::ostream& o) const { 
			if (first_) {
				o << "(" ; first_->output(o);  o<< ")";
			};
			o << "+";
			if (second_) {
				o << "(" ; second_->output(o);  o<< ")";
			};
			

		};

		virtual result_type operator()(argument_type data) const {
			if (first_ && second_)
				return (*first_)(data)+(*second_)(data);
			return result_type();
		};
		virtual smart_ptr_type Create_Instance() const {
			return Create_Instance_helper<OS_plus>();
		};
	};

template<class A, class R = A>
	struct OS_minus
		: public OS_binary_function<A, R>
	{	
		typedef OS_binary_function<A,R>	Base;
		OS_minus(){};
		OS_minus(const Base_Cast &first, const Base_Cast &second) 
			: Base(first,second)
		{};
		virtual std::string get_name() const {return "-";};
		virtual result_type operator()(argument_type data) const {
			if (first_ && second_)
				return (*first_)(data)-(*second_)(data);
			return result_type();
		};
		virtual bool equals (const Base_Cast & from) const {
			return check_equal(from);
		};

		virtual smart_ptr_type Create_Instance() const {
			return Create_Instance_helper<OS_minus>();
		};
		virtual void output (std::ostream& o) const { 
			if (first_) {
				o << "(" ; first_->output(o);  o<< ")";
			};
			o << "-";
			if (second_) {
				o << "(" ; second_->output(o);  o<< ")";
			};
			

		};

	};

template<class A, class R = A>
	struct OS_multiply
		: public OS_binary_function<A, R>
	{	
		typedef OS_binary_function<A,R>	Base;
		OS_multiply(){};
		OS_multiply(const Base_Cast &first, const Base_Cast &second) 
			: Base(first,second)
		{};
		virtual std::string get_name() const {return "*";};
		virtual result_type operator()(argument_type data) const {
			if (first_ && second_)
				return (*first_)(data)*(*second_)(data);
			return result_type();
		};
		virtual bool equals (const Base_Cast & from) const {
			return check_equal(from);
		};
		virtual smart_ptr_type Create_Instance() const {
			return Create_Instance_helper<OS_multiply>();
		};
		virtual void output (std::ostream& o) const { 
			if (first_) {
				o << "(" ; first_->output(o);  o<< ")";
			};
			o << "*";
			if (second_) {
				o << "(" ; second_->output(o);  o<< ")";
			};
			
		};

	};

template<class A, class R = A>
	struct OS_divide
		: public OS_binary_function<A, R>
	{	
		typedef OS_binary_function<A,R>	Base;
		OS_divide(){};
		OS_divide(const Base_Cast &first, const Base_Cast &second) 
			: Base(first,second)
		{};
		virtual std::string get_name() const {return "/";};
		virtual result_type operator()(argument_type data) const {
			if (first_ && second_)
				return (*first_)(data)/(*second_)(data);
			return result_type();
		};
		virtual bool equals (const Base_Cast & from) const {
			return check_equal(from);
		};
		virtual smart_ptr_type Create_Instance() const {
			return Create_Instance_helper<OS_divide>();
		};
		virtual void output (std::ostream& o) const { 
			if (first_) {
				o << "(" ; first_->output(o);  o<< ")";
			};
			o << "*";
			if (second_) {
				o << "(" ; second_->output(o);  o<< ")";
			};
			
		};

	};
template<class A, class R = A>
	struct OS_xor
		: public OS_binary_function<A, R>
	{	
		typedef OS_binary_function<A,R>	Base;
		OS_xor(){};
		OS_xor(const Base_Cast &first, const Base_Cast &second) 
			: Base(first,second)
		{};
		virtual std::string get_name() const {return "^";};
		virtual result_type operator()(argument_type data) const {
			if (first_ && second_)
				return ( (*first_)(data) ) ^ ( (*second_) (data) );
			return result_type();
		};
		virtual bool equals (const Base_Cast & from) const {
			return check_equal(from);
		};
		virtual smart_ptr_type Create_Instance() const {
			return Create_Instance_helper<OS_xor>();
		};
		virtual void output (std::ostream& o) const { 
			if (first_) {
				o << "(" ; first_->output(o);  o<< ")";
			};
			o << "^";
			if (second_) {
				o << "(" ; second_->output(o);  o<< ")";
			};
			
		};

	};

#endif
